#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <ESP8266HTTPClient.h>

// 电脑服务端的 ip （局域网内的电脑 ip + 监听的端口）
#define SERVER_IP "192.168.0.103:3000"

// WiFi 的名称和密码
#ifndef STASSID
#define STASSID "Tenda_30F5D8"
#define STAPSK  "qwertyuiop"
#endif

const char* ssid = STASSID;
const char* password = STAPSK;

ESP8266WebServer server(80); // 监听 80 端口

const int stepTime = 10; // 每轮循环的休息时间 / 毫秒
const int checkIntervalMinute = 5; // 检查土壤时间间隔 / 分钟
const int led = 13; // 模块自带的 led 灯
const int sensorPin = A0; // 传感器信号输入引脚 A0 
const int realyPin = D1; // 继电器控制引脚 D1
const int sensorLimit = 700; // 感应器数值界限
const int cooldown = checkIntervalMinute * 60 * 1 * ( 1000 / stepTime ); // 每轮重置的冷却点数 
int currentCooldown = cooldown; // 当前冷却点数（ 每轮循环当前冷却点数减等于步长
int lastSensorValue = 0; // 上一次传感器的读数
String baseUrl = "http://" SERVER_IP; // http 请求的 url

// ---------------------------- 路由 -------------------------
// 访问根路由做的事
void handleRoot() {
  digitalWrite(led, 1);
  server.send(200, "text/plain", "automatic watering");
  digitalWrite(led, 0);
}

// 找不到这个路由 404 
void handleNotFound() {
  digitalWrite(led, 1);
  String message = "File Not Found\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET) ? "GET" : "POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";
  for (uint8_t i = 0; i < server.args(); i++) {
    message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
  }
  server.send(404, "text/plain", message);
  digitalWrite(led, 0);
}

// 强制浇水
void handleBeginWater() {
  digitalWrite(led, 1);
  beginDrawWater();
  server.send(200, "text/plain", "watering");
  Serial.println("\n浇水中\n");
  delay(5 * 1000);
  digitalWrite(led, 0);
}

// 获取当前的传感器参数
void handleReadSensor() {
  digitalWrite(led, 1);
  // int sensorValueStr = String(readSensor());
  char sensorValueStr[25]; 
  itoa(readSensor(), sensorValueStr, 10); 
  Serial.println("\n请求当前传感器数值\n");
  // server.send(200, "application/json", sensorValueStr);
  server.send(200, "text/plain", sensorValueStr);
  digitalWrite(led, 0);
}

// ---------------------------- 初始化 -------------------------
// 连接 wifi
void connectWifi() {
  
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.println("");

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
}

// 初始化服务器，FIXME: 路由注册在这里
void serverInit() {

  if (MDNS.begin("esp8266")) {
    Serial.println("MDNS responder started");
  }

  server.on("/", handleRoot);

  server.on("/hello", []() {
    server.send(200, "text/plain", "When you gaze long into the abyss, the abyss is looking back into you.");
  });

  server.on("/water", handleBeginWater);

  server.on("/read-sensor", handleReadSensor);

  server.onNotFound(handleNotFound);

  server.begin();
  Serial.println("HTTP server started");
}

// 输入输出初始化
void ioInit() {
  pinMode(led, OUTPUT);
  digitalWrite(led, 0);
  pinMode(realyPin, OUTPUT);
  pinMode(sensorPin, INPUT);

  Serial.begin(115200);
}

// ---------------------------- 其它封装 -------------------------
// 发起 HTTP GET 请求
void requestGetRouter(String router) {

    WiFiClient client;
    HTTPClient http;

    Serial.print("[HTTP] begin...\n");
    // configure traged server and url
    http.begin(client, baseUrl + router); //HTTP
    Serial.print(baseUrl + router);

    http.addHeader("Content-Type", "application/json");

    Serial.print("\n[HTTP] GET...\n");

    // start connection and send HTTP header and body
    int httpCode = http.GET();

    // httpCode will be negative on error
    if (httpCode > 0) {
      // HTTP header has been send and Server response header has been handled
      Serial.printf("[HTTP] GET... code: %d\n", httpCode);

      // file found at server
      if (httpCode == HTTP_CODE_OK) {
        const String& payload = http.getString();
        Serial.println("received payload:\n<<");
        Serial.println(payload);
        Serial.println(">>");
      }
    } else {
      Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
    }

    http.end();
}

// ---------------------------- 业务 -------------------------
// 开始抽水
void beginDrawWater() {
  digitalWrite(realyPin, LOW);
}

// 停止抽水
void stopDrawWater() {
  digitalWrite(realyPin, HIGH);
}

// 读取传感器
int readSensor() {
  int sensorValue = analogRead(sensorPin);
  return sensorValue;
}

// 测试能否连接到服务器 （ 初始化都完成后测试
void connectTest() {
  requestGetRouter("/api/connect-test");
}

// 通知服务端记录日志
void keepLog(int sensorValue) {
  if (abs(sensorValue - lastSensorValue) > 1) { // 忽略小波动
    char sensorValueStr[25]; 
    char url[] = "/api/record/?sensor-value=";
    itoa(sensorValue, sensorValueStr, 10); 
    // Serial.println("last");
    // Serial.print(sensorValueStr);
    strcat(url, sensorValueStr);
    requestGetRouter(url);
    lastSensorValue = sensorValue;
  }
}

// 扫描传感器
void scanSensor() {
  int sensorValue = readSensor();
  // Serial.println(sensorValue);
  if (sensorValue < sensorLimit) { // 传感器的值小于界限值
    beginDrawWater();
  } else {
    stopDrawWater();
  }

  if (currentCooldown < 0) {
    currentCooldown = cooldown;
    keepLog(sensorValue);
  } else {
    currentCooldown -= stepTime;
  }
}

// ---------------------------- main -------------------------
void setup(void) {
  ioInit();
  stopDrawWater();
  connectWifi();
  serverInit();
  delay(5 * 1000);
  connectTest();
}

void loop(void) {
  scanSensor();
  server.handleClient();
  MDNS.update();
  delay(stepTime);
}
